import * as vscode from 'vscode'

/**
 * 代码块开始关键字数组
 * 用于判断是否需要增加缩进级别
 */
const blockStarts = [
	'(', 'INPUT', 'MENU', 'GLOBALS', 'MAIN', 'IF', 'REPORT', 'FUNCTION', 'FOR', 'PRIVATE',
	'WHILE', 'FOREACH', 'CASE'
]

const blockStartsSet = new Set(blockStarts)
/**
 * 需要大写的关键字数组
 * 这些关键字在格式化时会被转换为大写
 */
const keywords = [
	'END', 'INPUT', 'MENU', 'GLOBALS', 'MAIN', 'IF', 'REPORT', 'FUNCTION', 'FOR',
	'WHILE', 'FOREACH', 'CASE', 'RECORD', 'PRINT', 'AFTER', 'BEFORE', 'ON', 'ELSE',
	'BEGIN', 'COMMIT', 'WORK', 'ROLLBACK', 'ERROR', 'LET', 'CALL', 'MESSAGE', 'AT',
	'PROMPT', 'DISPLAY', 'DEFINE', 'ARRAY', 'DECLARE', 'CURSOR', 'INT', 'DEC',
	'CHAR', 'SELECT', 'FROM', 'FORM', 'WHERE', 'AND', 'OR', 'DATE', 'ROUND', 'SUM',
	'INTEGER', 'OUTPUT', 'OPTIONS', 'FORMAT', 'DATABASE', 'AS', 'ORDER', 'BY',
	'INITIALIZE', 'TO', 'NULL', 'LIKE', 'CONSTRUCT', 'NAME', 'EXIT', 'PROGRAM',
	'DECIMAL', 'CLIPPED', 'PREPARE', 'START', 'FINISH', 'RUN', 'INTO', 'SET_COUNT',
	'KEY', 'LOCK', 'SKIP', 'TO', 'TOP', 'OF', 'PAGE', 'LINE', 'CLOSE', 'SMALLINT',
	'DEFER', 'INTERRUPT', 'INSERT', 'UPDATE', 'DELETE', 'CONTROL', 'STARTLOG',
	'INTO', 'OPEN', 'WINDOW', 'ATTRIBUTE', 'BORDER', 'COMMAND', 'WITH', 'ROWS',
	'COLUMNS', 'TIME', 'TODAY', 'INFIELD', 'FIELD', 'NEXT', 'THEN', 'FETCH',
	'FIRST', 'PREVIOUS', 'LAST', 'STATUS', 'WITHOUT', 'DEFAULTS', 'WHEN',
	'OTHERWISE', 'TRUE', 'FALSE', 'CLEAR', 'RETURN', 'RETURNING', 'PRIVATE', 'NOT'
]

/**
 * 生成换行符函数
 * @param n 换行符数量，默认为1
 * @returns 换行符字符串
 */
const keywordsSet = new Set(keywords)
const LBC = (n: number = 1): string => '\r\n'.repeat(n)

/**
 * 生成缩进空格函数
 * 根据缩进级别生成相应数量的空格
 * @param indent_level 缩进级别
 * @returns 缩进空格字符串
 */
const indent = (indent_level: number): string => {
	const cfg = vscode.workspace.getConfiguration()
	const tabFromCfg = cfg.get('TabLength') as number | undefined
	const tabFromEditor = vscode.window.activeTextEditor?.options.tabSize as number | undefined
	const tablength = (typeof tabFromCfg === 'number' && tabFromCfg > 0 ? tabFromCfg
		: typeof tabFromEditor === 'number' && tabFromEditor > 0 ? tabFromEditor
		: 4)
	return ' '.repeat(indent_level * tablength)
}

/**
 * 格式化源代码函数
 * 对4GL代码进行格式化处理
 * @param source 源代码字符串
 * @returns 格式化后的代码字符串
 */
const format = (source: string): string => {
	let output = ''
	// 缩进级别
	let indent_level = 0
	// 分成多行
	const lines = source.split(/\r?\n/)
	
	// 处理每一行
	for (const line of lines) {
		// 去掉行首和行尾的空格
		const trimmedLine = line.trim()
		// 当前行不是空行才继续处理
		if (trimmedLine !== '' && trimmedLine !== '\n') {
			// 仅对引号外、注释符 # 之前的代码进行关键字大写；字符串与注释保持原样
			const findUnquotedHash = (s: string): number => {
				let inSingle = false
				let inDouble = false
				for (let i = 0; i < s.length; i++) {
					const ch = s[i]
					if (ch === '\'' && !inDouble) inSingle = !inSingle
					else if (ch === '"' && !inSingle) inDouble = !inDouble
					else if (ch === '#' && !inSingle && !inDouble) return i
				}
				return -1
			}
			const rawLine = line
			const commentIdx = findUnquotedHash(rawLine)
			const codePart = commentIdx >= 0 ? rawLine.slice(0, commentIdx) : rawLine
			const commentPart = commentIdx >= 0 ? rawLine.slice(commentIdx) : ''

			const codeTrimmed = codePart.trim()
			// 分割成单词（忽略引号内空白）
			const tokens = codeTrimmed.split(/(?<!\\)\s+(?=(?:[^"]*"[^"]*")*[^"]*$)(?=(?:[^']*'[^']*')*[^']*$)/)

			// 保存处理后的当前行
			let outline = ''
			// 大写关键字（跳过含引号的 token，避免修改字符串内容）
			for (const token of tokens) {
				if (token !== '') {
					if (token.includes('"') || token.includes('\'')) {
						outline += token + ' '
					} else {
						const upper = token.toUpperCase()
						outline += (keywordsSet.has(upper) ? upper : token) + ' '
					}
				}
			}
			// 拼回注释部分
			outline = (outline.trimEnd() + (commentPart ? ' ' + commentPart.trimEnd() : '')).trimEnd()
			
			// 如果是END或者)结尾下面的要减少缩进
			if ((tokens[0] || '').toUpperCase() === 'END' || tokens[0] === ')') {
				if (indent_level > 0) indent_level--
			}
			
			// 用这个标记是不是进入ELSE, OPTIONS, OUTPUT, FORMAT, AFTER, BEFORE, WHEN, ON的缩进
			let lessIndentThisLine = false
			// 如果是ELSE, OPTIONS, OUTPUT, FORMAT, AFTER, BEFORE, WHEN, ON结尾下面的行要减少缩进
			if ([
				'ELSE', 'OPTIONS', 'OUTPUT', 'FORMAT', 'AFTER', 'BEFORE', 'WHEN', 'ON'
			].includes((tokens[0] || '').toUpperCase())) {
				if (indent_level > 0) indent_level--
				lessIndentThisLine = true
			}
			
			// 处理每一行的缩进和换行
			switch ((tokens[0] || '').toUpperCase()) {
				case 'SELECT':
				case 'INSERT':
				case 'UPDATE':
				case 'DELETE':
				case 'IF':
				case 'WHILE':
				case 'FOR':
				case 'CASE':
				case 'WHEN':
				case 'OTHERWISE':
				case 'RETURN':
				case 'RETURNING':
				case 'FOREACH':
					output += `${LBC()}${indent(indent_level)}${outline.trimEnd()}${LBC()}`
					break
				case 'FUNCTION':
				case 'PRIVATE':
				case 'END':
					output += `${indent(indent_level)}${outline.trimEnd()}${LBC(2)}`
					break
				default:
					output += `${indent(indent_level)}${outline.trimEnd()}${LBC()}`
					break
			}
			
			// 如果进入了ELSE, OPTIONS, OUTPUT, FORMAT, AFTER, BEFORE, WHEN, ON，则下一行要缩进
			if (lessIndentThisLine) {
				indent_level++
			}

			if (![
				'END', ')'
			].includes((tokens[0] || '').toUpperCase()) && !trimmedLine.match(/\s(END|end)\s/) && (
					blockStartsSet.has((tokens[0] || '').toUpperCase()) || trimmedLine.match(/(record|RECORD)$/)
				)) {
				//如果是 INPUT NO WRAP 下一行也不用缩进了
				if ('INPUT' === (tokens[0] || '').toUpperCase() && tokens.length > 1) {
					if ((tokens[1] || '').toUpperCase() === 'WRAP'|| (tokens[1] || '').toUpperCase() === 'NO') {
						continue
					}		
				}
				// 因为 GLOBALS 有2种情况,如果后面跟字符串的就不用缩进了
				if ('GLOBALS' === (tokens[0] || '').toUpperCase() && tokens.length > 1) {
					continue
				} else {
					indent_level++
				}
			}
		}
	}

	return output
}

/**
 * 文档格式化编辑提供者类
 * 为4GL语言提供代码格式化功能
 */
export class MyDocumentFormattingEditProvider implements vscode.DocumentFormattingEditProvider {
	/**
	 * 提供文档格式化编辑函数
	 * 对整个文档进行格式化
	 * @param document 当前文档
	 * @param options 格式化选项
	 * @param token 取消令牌
	 * @returns 文本编辑数组
	 */
	provideDocumentFormattingEdits(
		document: vscode.TextDocument, 
		options: vscode.FormattingOptions, 
		token: vscode.CancellationToken
	): vscode.ProviderResult<vscode.TextEdit[]> {
		const text = document.getText()
		const range = new vscode.Range(
			document.positionAt(0),
			document.positionAt(text.length)
		)
		return Promise.resolve([new vscode.TextEdit(range, format(text))])
	}
}

/**
 * 删除所有行注释函数
 * 删除当前文件或选中内容中的所有单行注释
 */
export const delAllLineNote = async () => {
	const editor = vscode.window.activeTextEditor
	if (!editor) {
		return
	}

	await vscode.window.withProgress({
		location: vscode.ProgressLocation.Notification,
		title: "删除单行注释中...",
		cancellable: true
	}, async (progress) => {
		const code = editor.document.getText(editor.selection)
		
		if (code) {
			progress.report({ increment: 30, message: "处理选中内容..." })
			
			const lines = code.split(/\r?\n/)
			const newLines = []
			
			// 根据行数调整进度更新频率
			const updateInterval = Math.max(1, Math.floor(lines.length / 10))
			
			for (let i = 0; i < lines.length; i++) {
				const text = lines[i].trimStart()
				if (!text.startsWith('#')) {
					newLines.push(lines[i])
				}
				
				// 动态调整进度更新频率
				if (i % updateInterval === 0) {
					progress.report({ increment: 40 * (i / lines.length) })
				}
			}

			await editor.edit(editBuilder => {
				editBuilder.replace(editor.selection, newLines.join('\n'))
			})
			
		} else {
			progress.report({ increment: 30, message: "处理整个文件..." })
			
			const content = editor.document.getText()
			const lines = content.split(/\r?\n/)
			const deleteRanges = []
			
			// 根据行数调整进度更新频率
			const updateInterval = Math.max(1, Math.floor(lines.length / 10))
			
			for (let i = 0; i < lines.length; i++) {
				const text = lines[i].trimStart()
				if (text.startsWith('#')) {
					deleteRanges.push(new vscode.Range(
						new vscode.Position(i, 0),
						new vscode.Position(i, lines[i].length)
					))
				}
				
				// 动态调整进度更新频率
				if (i % updateInterval === 0) {
					progress.report({ increment: 40 * (i / lines.length) })
				}
			}

			await editor.edit(editBuilder => {
				deleteRanges.forEach(range => {
					editBuilder.delete(range)
				})
			})
		}
	})
}